#include <iostream>

#include "gtest/gtest.h"
#include "ClientApi.h"
#include <memory>
#include "Log.h"
#include <vector>
#include <boost/filesystem.hpp>
using namespace CSApi;
static boost::shared_ptr<CSApi::ClientApi> api;

#define STORAGE_NAME "Storage"

class Share : public ::testing::Test {
	protected:

		static void SetUpTestCase()	{
			FUNCTION_TRACK
			ASSERT_NO_THROW({
				api = CreateClientApi("127.0.0.1", "5001", "root", "root");
			});
            ASSERT_TRUE(api.get()!=NULL);
		}

		static void TearDownTestCase() {
			ASSERT_NO_THROW({
				api.reset();
                boost::filesystem::remove_all("/tmp/Share");
			});
		}
};

TEST_F(Share, Share_FailedEmptyArguments) {
    FUNCTION_TRACK
    ASSERT_TRUE(api.get()!=NULL);
    EXPECT_THROW({
        api->Share("","","");
    }, CSException_WrongArgument);
}

TEST_F(Share, Share_FailedWrongStorageName) {
    FUNCTION_TRACK
    ASSERT_TRUE(api.get()!=NULL);
    EXPECT_THROW({
        api->Share("/a","/a","sfhcnwnavwvwvan");
    }, CSException_WrongArgument);
}

TEST_F(Share, Share_FailedWrongStoragePath) {
    FUNCTION_TRACK
    ASSERT_TRUE(api.get()!=NULL);
    EXPECT_THROW({
        api->Share("/a","<>dupa","STORAGE_NAME");
    }, CSException_WrongArgument);
    EXPECT_THROW({
        api->Share("/a","/tmp/../../dupa/","STORAGE_NAME");
    }, CSException_WrongArgument);
    EXPECT_THROW({
        api->Share("/a","./dupa","STORAGE_NAME");
    }, CSException_WrongArgument);
}

TEST_F(Share, Share_FailedWrongVirtualPath) {
    FUNCTION_TRACK
    ASSERT_TRUE(api.get()!=NULL);
    EXPECT_THROW({
        api->Share("C:\\","/a",STORAGE_NAME);
    }, CSException_WrongArgument);
    EXPECT_THROW({
        api->Share("/dupa/|","/a",STORAGE_NAME);
    }, CSException_WrongArgument);
    EXPECT_THROW({
        api->Share("/dupa/<","/a",STORAGE_NAME);
    }, CSException_WrongArgument);
    EXPECT_THROW({
        api->Share("/dupa/>","/a",STORAGE_NAME);
    }, CSException_WrongArgument);
    EXPECT_THROW({
        api->Share("/dupa/?","/a",STORAGE_NAME);
    }, CSException_WrongArgument);
    EXPECT_THROW({
        api->Share("/dupa/*","/a",STORAGE_NAME);
    }, CSException_WrongArgument);
    EXPECT_THROW({
        api->Share("/dupa/ ha/","/a",STORAGE_NAME);
    }, CSException_WrongArgument);
    EXPECT_THROW({
        api->Share("/dupa/ha/../../../../folder","/a",STORAGE_NAME);
    }, CSException_WrongArgument);
}

TEST_F(Share, Share_FailedStorageOffline) {
    FUNCTION_TRACK
    ASSERT_TRUE(api.get()!=NULL);
    EXPECT_THROW({
        api->Share("/a","/a","Storage2");
    }, CSException_StorageOffline);
}

TEST_F(Share, Share_FailedStoragePathNotExists) {
    FUNCTION_TRACK
    ASSERT_TRUE(api.get()!=NULL);
    EXPECT_THROW({
        api->Share("/a","/tmp/super not exists folder",STORAGE_NAME);
    }, CSException_InvalidPath);
}

TEST_F(Share, Share_FailedStoragePathNoName) {
    FUNCTION_TRACK
    ASSERT_TRUE(api.get()!=NULL);
    EXPECT_THROW({
        api->Share("/","/",STORAGE_NAME, true);
    }, CSException_InvalidPath);
    EXPECT_THROW({
        api->Share("/","/tmp/",STORAGE_NAME, true);
    }, CSException_InvalidPath);
}

TEST_F(Share, Share_FailedFolderExists) {
    FUNCTION_TRACK
    ASSERT_TRUE(api.get()!=NULL);
    EXPECT_THROW({
        api->Share("/storage1","/",STORAGE_NAME);
    }, CSException_FolderExits);
}

TEST_F(Share, Share_SuccessSingleFolder) {
    FUNCTION_TRACK
    ASSERT_TRUE(api.get()!=NULL);
    ASSERT_NO_THROW({
        ASSERT_TRUE(api->Share("/tmp","/tmp",STORAGE_NAME));
    });
}

TEST_F(Share, Share_SuccessSingleFile) {
    FUNCTION_TRACK
    ASSERT_TRUE(api.get()!=NULL);
    boost::filesystem::create_directories("/tmp/Share");
    FILE* f = fopen("/tmp/Share/plik", "w");
    ASSERT_TRUE(f!=NULL);
    fclose(f);
    ASSERT_NO_THROW({
        ASSERT_TRUE(api->Share("/super-plik","/tmp/Share/plik",STORAGE_NAME));
    });
}

TEST_F(Share, UnShare_FailedInvalidPath) {
    FUNCTION_TRACK
    ASSERT_TRUE(api.get()!=NULL);
    EXPECT_THROW({
        api->UnShare("C:\\");
    }, CSException_WrongArgument);
    EXPECT_THROW({
        api->UnShare("/dupa/|");
    }, CSException_WrongArgument);
    EXPECT_THROW({
        api->UnShare("/dupa/<");
    }, CSException_WrongArgument);
    EXPECT_THROW({
        api->UnShare("/dupa/>");
    }, CSException_WrongArgument);
    EXPECT_THROW({
        api->UnShare("/dupa/?");
    }, CSException_WrongArgument);
    EXPECT_THROW({
        api->UnShare("/dupa/*");
    }, CSException_WrongArgument);
    EXPECT_THROW({
        api->UnShare("/dupa/ ha/");
    }, CSException_WrongArgument);
    EXPECT_THROW({
        api->UnShare("/dupa/ha/../../../../folder");
    }, CSException_WrongArgument);
}

TEST_F(Share, UnShare_FailedPathNotExists) {
    FUNCTION_TRACK
    ASSERT_TRUE(api.get()!=NULL);
    EXPECT_THROW({
        api->UnShare("/dupa");
    }, CSException_InvalidPath);
    EXPECT_THROW({
        api->UnShare("/dupa12312");
    }, CSException_InvalidPath);
    EXPECT_THROW({
        api->UnShare("/test");
    }, CSException_InvalidPath);
    EXPECT_THROW({
        api->UnShare("/test/supertest/");
    }, CSException_WrongArgument);
}

TEST_F(Share, UnShare_FailedDirectChild) {
    FUNCTION_TRACK
    ASSERT_TRUE(api.get()!=NULL);
    ASSERT_NO_THROW({
    boost::filesystem::create_directories("/tmp/Share/UnShare_FailedDirectChild/f1");
    boost::filesystem::create_directories("/tmp/Share/UnShare_FailedDirectChild/f2");
    boost::filesystem::create_directories("/tmp/Share/UnShare_FailedDirectChild/f2/f3");
    });

    EXPECT_NO_THROW({
        ASSERT_TRUE(api->Share("/UnShare_FailedDirectChild", "/tmp/Share/UnShare_FailedDirectChild", STORAGE_NAME));
    });

    EXPECT_THROW({
        api->UnShare("/UnShare_FailedDirectChild/f1");
    }, CSException_InvalidOperation);

    EXPECT_THROW({
        api->UnShare("/UnShare_FailedDirectChild/f1/");
    }, CSException_InvalidOperation);

    EXPECT_THROW({
        api->UnShare("/UnShare_FailedDirectChild/f2");
    }, CSException_InvalidOperation);

    EXPECT_THROW({
        api->UnShare("/UnShare_FailedDirectChild/f2/f3");
    }, CSException_InvalidOperation);

	ASSERT_NO_THROW({
         boost::filesystem::remove_all("/tmp/Share/UnShare_FailedDirectChild");
	});
}

TEST_F(Share, UnShare_FailedPureVirtual) {
    FUNCTION_TRACK
    ASSERT_TRUE(api.get()!=NULL);
    EXPECT_THROW({
        api->UnShare("/");
    }, CSException_InvalidOperation);
}

TEST_F(Share, UnShare_SuccessSingleFolder) {
    FUNCTION_TRACK
    ASSERT_TRUE(api.get()!=NULL);
    boost::filesystem::create_directories("/tmp/Share/UnShare_SingleSuccess/f1");
    boost::filesystem::create_directories("/tmp/Share/UnShare_SingleSuccess/f2");
    boost::filesystem::create_directories("/tmp/Share/UnShare_SingleSuccess/f2/f3");
    EXPECT_NO_THROW({
        EXPECT_TRUE(api->Share("/UnShare_SingleSuccess","/tmp/Share/UnShare_SingleSuccess", STORAGE_NAME));
    });

	EXPECT_NO_THROW({
         EXPECT_TRUE(api->UnShare("/UnShare_SingleSuccess"));
	});

	ASSERT_NO_THROW({
         boost::filesystem::remove_all("/tmp/Share/UnShare_SingleSuccess");
	});
}

TEST_F(Share, UnShare_SuccessSingleFile) {
    FUNCTION_TRACK
    ASSERT_TRUE(api.get()!=NULL);
    boost::filesystem::create_directories("/tmp/Share");
    FILE* f = fopen("/tmp/Share/plik2", "w");
    ASSERT_TRUE(f!=NULL);
    fclose(f);
    ASSERT_NO_THROW({
        ASSERT_TRUE(api->Share("/super-plik2","/tmp/Share/plik2",STORAGE_NAME));
    });

	EXPECT_NO_THROW({
         EXPECT_TRUE(api->UnShare("/super-plik2"));
	});
}

TEST_F(Share, UnShare_SuccessMulti) {
    FUNCTION_TRACK
    ASSERT_TRUE(api.get()!=NULL);
    boost::filesystem::create_directories("/tmp/Share/UnShare_MultiSuccess/f1");
    boost::filesystem::create_directories("/tmp/Share/UnShare_MultiSuccess/f2");
    boost::filesystem::create_directories("/tmp/Share/UnShare_MultiSuccess/f2/f3");
    boost::filesystem::create_directories("/tmp/Share/UnShare_MultiSuccess2/f1");
    boost::filesystem::create_directories("/tmp/Share/UnShare_MultiSuccess2/f2");
    boost::filesystem::create_directories("/tmp/Share/UnShare_MultiSuccess2/f2/f3");

    EXPECT_NO_THROW({
        ASSERT_TRUE(api->Share("/UnShare_MultiSuccess","/tmp/Share/UnShare_MultiSuccess", STORAGE_NAME));
    });
    EXPECT_NO_THROW({
        ASSERT_TRUE(api->Share("/UnShare_MultiSuccess/f4","/tmp/Share/UnShare_MultiSuccess2", STORAGE_NAME));
    });

	EXPECT_NO_THROW({
         EXPECT_TRUE(api->UnShare("/UnShare_MultiSuccess"));
	});

	EXPECT_NO_THROW({
         EXPECT_TRUE(api->UnShare("/UnShare_MultiSuccess/f4"));
	});

	ASSERT_NO_THROW({
         boost::filesystem::remove_all("/tmp/Share/UnShare_SingleSuccess");
         boost::filesystem::remove_all("/tmp/Share/UnShare_SingleSuccess2");
	});
}

TEST_F(Share, UnShare_SuccessMultiFull) {
    FUNCTION_TRACK
    ASSERT_TRUE(api.get()!=NULL);
    boost::filesystem::create_directories("/tmp/Share/UnShare_MultiSuccessFull/f1");
    boost::filesystem::create_directories("/tmp/Share/UnShare_MultiSuccessFull/f2");
    boost::filesystem::create_directories("/tmp/Share/UnShare_MultiSuccessFull/f2/f3");
    boost::filesystem::create_directories("/tmp/Share/UnShare_MultiSuccessFull/f1");
    boost::filesystem::create_directories("/tmp/Share/UnShare_MultiSuccessFull/f2");
    boost::filesystem::create_directories("/tmp/Share/UnShare_MultiSuccessFull/f2/f3");
    EXPECT_NO_THROW({
        ASSERT_TRUE(api->Share("/UnShare_MultiSuccessFull","/tmp/Share/UnShare_MultiSuccessFull", STORAGE_NAME));
    });
    EXPECT_NO_THROW({
        ASSERT_TRUE(api->Share("/UnShare_MultiSuccessFull/f4","/tmp/Share/UnShare_MultiSuccessFull", STORAGE_NAME));
    });

	EXPECT_NO_THROW({
         EXPECT_TRUE(api->UnShare("/UnShare_MultiSuccessFull", true));
	});

	EXPECT_THROW({
         api->UnShare("/UnShare_MultiSuccessFull/f4");
	}, CSException_WrongArgument);

	ASSERT_NO_THROW({
         boost::filesystem::remove_all("/tmp/Share/UnShare_MultiSuccessFull");
         boost::filesystem::remove_all("/tmp/Share/UnShare_MultiSuccessFull");
	});
}